package com.skynet.app.api.auth.service.impl;

import org.springframework.stereotype.Service;


import com.skynet.app.api.auth.constant.ClaimsConstant;
import com.skynet.app.api.auth.dto.UserAuthDTO;
import com.skynet.app.api.auth.repository.UserAuthRepository;
import com.skynet.app.api.auth.service.UserAuthService;
import com.skynet.app.api.auth.util.JwtTokenUtil;
import com.skynet.app.api.auth.config.SmtpMailProperties;
import com.skynet.app.api.auth.entity.User;
import com.skynet.app.api.auth.exception.UserAuthException;
import com.skynet.app.api.auth.util.JsonUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import static com.skynet.app.api.auth.enums.ResultEnum.*;

/**
 * @author Skynet
 * @date 2017年11月23日 12:02
 */
@Service
@Slf4j
public class UserAuthServiceImpl implements UserAuthService {

    @Autowired
    private UserAuthRepository userAuthRepository;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private SmtpMailProperties smtpMailProperties;

    @Autowired
    private AmqpTemplate rabbitTemplate;

    @Override
    public String login(UserAuthDTO userAuthDTO) {
        String email = userAuthDTO.getEmail();
        String password = userAuthDTO.getPassword();
        try {
            UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(email, password);
            Authentication authentication = authenticationManager.authenticate(upToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            //查询数据库获取用户信息
            User userAuth = userAuthRepository.findByEmail(email);
            Map<String, Object> claims = new HashMap<>();
            claims.put(ClaimsConstant.CLAIM_KEY_FULLNAME, userAuth.getFullName());
            claims.put(ClaimsConstant.CLAIM_KEY_EMAIL, userAuth.getEmail());
            claims.put(ClaimsConstant.CLAIM_KEY_PICTURE, userAuth.getPicture());
            log.info("【用户登录】用户登录成功，email={}", email);
            return JwtTokenUtil.generateToken(claims);
        } catch (AuthenticationException e) {
            log.error("【用户登录】邮箱或密码错误");
            throw new UserAuthException(AUTHENTICATION_ERROR);
        }
    }

    @Override
    public void register(UserAuthDTO userAuthDTO) {
        if (userAuthRepository.findByEmail(userAuthDTO.getEmail()) != null) {
            log.error("【用户注册】邮箱已经被注册，userAuthDTO={}", JsonUtil.toJson(userAuthDTO));
            throw new UserAuthException(EMAIL_IS_ALREADY_REGISTERED);
        }
        userAuthDTO.setPassword(bCryptPasswordEncoder.encode(userAuthDTO.getPassword()));
        User userAuth = new User();
        BeanUtils.copyProperties(userAuthDTO, userAuth);
        User user = userAuthRepository.save(userAuth);
        if (user.getId() == null) {
            log.error("【用户注册】用户注册失败，userAuthDTO={}", JsonUtil.toJson(userAuthDTO));
            throw new UserAuthException(USER_REGISTER_ERROR);
        }
        log.info("【用户注册】用户注册成功，userAuthDTO={}", JsonUtil.toJson(userAuthDTO));
    }

    @Override
    public void logout() {
        log.info("【用户注销】注销成功！");
    }

    @Override
    public void resetPass(UserAuthDTO userAuthDTO) {
        String passwordToken = userAuthDTO.getResetPasswordToken();
        if (StringUtils.isEmpty(passwordToken)) {
            log.error("【重置密码】重置失败，reason={}", TOKEN_IS_EMPTY.getMsg());
            throw new UserAuthException(TOKEN_IS_EMPTY);
        }
        Claims claims = JwtTokenUtil.parseToken(passwordToken);
        if (claims == null) {
            log.error("【重置密码】重置失败，reason={}", TOKEN_PARSE_ERROR.getMsg());
            throw new UserAuthException(TOKEN_PARSE_ERROR);
        }
        String email = String.valueOf(claims.get("email"));
        log.info("【用户鉴权】email={}", email);
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(email, null, new ArrayList<>());
        SecurityContextHolder.getContext().setAuthentication(authentication);

        User userAuth = userAuthRepository.findByEmail(email);
        userAuth.setPassword(bCryptPasswordEncoder.encode(userAuthDTO.getPassword()));
        User user = userAuthRepository.save(userAuth);
        if (user.getId() == null) {
            log.error("【重置密码】更新用户信息失败，userAuthDTO={}", JsonUtil.toJson(userAuth));
            throw new UserAuthException(UPDATE_USER_ERROR);
        }
        log.info("【重置密码】重置密码成功");
    }

    @Override
    public void requestPass(UserAuthDTO userAuthDTO) {
        String email = userAuthDTO.getEmail();
        User userAuth = userAuthRepository.findByEmail(email);
        if (userAuth == null) {
            log.error("【取回密码】邮箱未注册，email={}", email);
            throw new UserAuthException(USER_NOT_FOUND_BY_EMAIL);
        }
        userAuth.setPassword(bCryptPasswordEncoder.encode(smtpMailProperties.getInitPass()));
        User user = userAuthRepository.save(userAuth);
        if (user.getId() == null) {
            log.error("【取回密码】更新用户信息失败，userAuthDTO={}", JsonUtil.toJson(userAuth));
            throw new UserAuthException(UPDATE_USER_ERROR);
        }
        log.info("【取回密码】取回密码成功");
        rabbitTemplate.convertAndSend("sendEmail",email);
    }
}
